package de.thm.exa.tests;
 
import de.thm.exa.algos.CuckooHash;
import de.thm.exa.exceptions.HashException;
import de.thm.exa.helpers.FileReader;
import de.thm.exa.helpers.FileWriter;
import de.thm.exa.helpers.Hash;
import de.thm.exa.holders.Page;
import de.thm.exa.interfaces.TestInterface;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
 
public class CuckooTest implements TestInterface
{
    
    @Override
    public void test() throws IOException
    {
        BufferedReader reader = new BufferedReader( new InputStreamReader(System.in) );
        
        boolean correctInput1 = false, correctInput2 = false;
        int input1 = Hash.CRC32, input2 = Hash.ADLER32;
        
        while(!correctInput1)
        {
            printInput1();
            try
            {
                input1 = Integer.parseInt( reader.readLine() );
            } catch(NumberFormatException e) {
                printError();
                continue;
            }
            
            if( input1 == 8 )
            {
                cuckooTestAll(true);
                reader.close();
                return;
            } else if( input1 == 9 ) {
                cuckooTestAll(false);
                reader.close();
                return;
            } else if( input1 < 8 && input1 > 0 ) {
                System.out.println( "1. Algorithm: " + Hash.toString(input1 - 1) );
                System.out.println("Next Algorithm");
                correctInput1 = true;
            } else if(input1 == 10) {
                reader.close();
                return;
            } else {
                printError();
                continue;
            }
        }
        while(!correctInput2)
        {
            printInput2();
            try
            {
                input2 = Integer.parseInt( reader.readLine() );
            } catch(NumberFormatException e) {
                printError();
                continue;
            }
            
            if( input2 < 8 && input2 > 0 )
            {
                System.out.println( "2. Algorithm: " + Hash.toString(input2 - 1) );
                correctInput2 = true;
            } else if(input1 == 9) {
                reader.close();
                return;
            } else {
                printError();
                continue;
            }
        }
        
        ArrayList<Page> pages = readPages();
        
        File output = new File(DEST_FILE);
        FileWriter.writeHeader( output );
        
        CuckooHash hasher = new CuckooHash(input1-2, input2-2, true);
        cuckooTest(hasher, pages, output);
        
        reader.close();
    }
    
    private void printInput1()
    {
        System.out.println(
                "Enter a number:\n"
              + "  1: " + Hash.toString(0) + "\n"
              + "  2: " + Hash.toString(1) + "\n"
              + "  3: " + Hash.toString(2) + "\n"
              + "  4: " + Hash.toString(3) + "\n"
              + "  5: " + Hash.toString(4) + "\n"
              + "  6: " + Hash.toString(5) + "\n"
              + "  7: " + Hash.toString(6) + "\n"
              + "  8: All Tests (WITH rehash)\n"
              + "  9: All Tests (WITHOUT rehash)\n"
              + "  10: Close Program\n");
    }
    
    private void printInput2()
    {
        System.out.println(
              "Enter a number:\n"
            + "  1: " + Hash.toString(0) + "\n"
            + "  2: " + Hash.toString(1) + "\n"
            + "  3: " + Hash.toString(2) + "\n"
            + "  4: " + Hash.toString(3) + "\n"
            + "  5: " + Hash.toString(4) + "\n"
            + "  6: " + Hash.toString(5) + "\n"
            + "  7: " + Hash.toString(6) + "\n"
            + "  8: Close Program\n");
    }
    
    private void printError()
    {
        System.out.println("This was not a correct input.");
    }
    
    private void cuckooTest(CuckooHash hasher, ArrayList<Page> pages, File output) throws IOException
    {
        hasher.setElapsedTime(0);
        long elapsedTime = 0;
        for(Page page : pages)
        {
            try
            {
                hasher.insert(page);
            } catch (HashException ex) {
                System.out.println( ex.getMessage() );
                hasher.setSucceeded(false);
                break;
            }
            elapsedTime += hasher.getElapsedTime();
        }
        
        hasher.setElapsedTime(elapsedTime);
        System.out.println( hasher.toString() );
        
        hasher.printFile( output );
    }
    
    private void cuckooTestAll(boolean rehash) throws IOException
    {
    	ArrayList<Page> pages = readPages();
    	
        File output = new File(DEST_FILE);
        FileWriter.writeHeader( output );

        int k = 1;
        for(int i = 0 ; i < Hash.FUNCTION_COUNT ; i++)
        {
            for(int j = 0 ; j < Hash.FUNCTION_COUNT ; j++)
            {
                if( i != j )
                {
                    CuckooHash hasher = new CuckooHash(i, j, rehash);
                    cuckooTest(hasher, pages, output);
                    
                    System.out.println("Test " + k + "/" + (int)(Math.pow(Hash.FUNCTION_COUNT,2)-Hash.FUNCTION_COUNT) + " completed\n" );
                    k++;
                }
            }
        }
    }
    
    private ArrayList<Page> readPages() throws IOException
    {
        File dir = new File(Hash.PATH);
        
        File[] files = dir.listFiles();


        int fileIndex = 1;
        ArrayList<Page> pages = new ArrayList<>();
        for( File file : files )
        {
        	int files_count = MAX_FILES < files.length ? MAX_FILES : files.length;
            System.out.println("Reading File: " + file.getName() + "[" + fileIndex + "/" + files_count + "]");
            pages.addAll( FileReader.read( file ) );
            fileIndex++;
            
            if ( fileIndex > MAX_FILES ) { break; }
        }
        return pages;
    }
}